'''
Author: duliang thinktanker@163.com
Date: 2024-06-24 17:27:21
LastEditors: duliang thinktanker@163.com
LastEditTime: 2025-09-27 23:26:19
FilePath: \sajj\error.py
Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
'''
import cv2
import numpy as np
from collections import deque


def detect_edges():
    # 打开摄像头
    cap = cv2.VideoCapture(0)

    if not cap.isOpened():
        print("无法打开摄像头")
        return

    # 用于存储上一次检测到的直线和边界框
    last_lines = None
    last_cropped = None
    last_valid_time = 0
    delay_duration = 1000  # 延时1秒(1000毫秒)

    # 用于存储最近10次最大的识别边界范围
    cropped_history = deque(maxlen=10)

    while True:
        # 读取帧
        ret, frame = cap.read()
        if not ret:
            print("无法获取帧")
            break

        # 转换为灰度图像
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)

        # 应用高斯模糊减少噪声
        blurred = cv2.GaussianBlur(gray, (5, 5), 0)

        # 使用Canny算法检测边缘
        edges = cv2.Canny(blurred, 50, 150)

        # 检测直线
        lines = cv2.HoughLinesP(edges,
                                1,
                                np.pi / 180,
                                threshold=100,
                                minLineLength=100,
                                maxLineGap=10)

        # 创建副本用于绘制直线
        line_frame = frame.copy()

        current_time = cv2.getTickCount() / cv2.getTickFrequency(
        ) * 1000  # 当前时间(毫秒)

        # 绘制检测到的直线
        if lines is not None:
            # 更新上一次检测到的直线
            last_lines = lines
            last_valid_time = current_time

            # 收集所有直线的端点坐标
            all_points = []
            for line in lines:
                x1, y1, x2, y2 = line[0]
                cv2.line(line_frame, (x1, y1), (x2, y2), (0, 255, 0), 2)
                all_points.append((x1, y1))
                all_points.append((x2, y2))

            # 计算包围所有直线端点的边界框
            if all_points:
                all_points = np.array(all_points)
                x_min, y_min = np.min(all_points, axis=0)
                x_max, y_max = np.max(all_points, axis=0)

                # 确保坐标在图像范围内
                x_min = max(0, x_min)
                y_min = max(0, y_min)
                x_max = min(frame.shape[1], x_max)
                y_max = min(frame.shape[0], y_max)

                # 提取并显示边界框内的图像
                if x_max > x_min and y_max > y_min:
                    last_cropped = frame[y_min:y_max, x_min:x_max]

                    # 将当前裁剪区域添加到历史记录中，包含时间戳
                    cropped_area = (x_max - x_min) * (y_max - y_min)
                    cropped_history.append({
                        'image':
                        last_cropped.copy(),
                        'area':
                        cropped_area,
                        'coords': (x_min, y_min, x_max, y_max),
                        'time':
                        current_time  # 添加时间戳
                    })

            # 优先显示能够近似封闭或者半封闭的内部图片
            # 查找轮廓
            contours, _ = cv2.findContours(edges, cv2.RETR_EXTERNAL,
                                           cv2.CHAIN_APPROX_SIMPLE)

            # 筛选出可能形成封闭区域的轮廓
            closed_regions = []
            for contour in contours:
                # 计算轮廓面积
                area = cv2.contourArea(contour)
                # 过滤掉太小的区域
                if area > 500:  # 可根据需要调整阈值
                    # 计算轮廓周长
                    perimeter = cv2.arcLength(contour, True)
                    # 计算轮廓近似
                    epsilon = 0.02 * perimeter
                    approx = cv2.approxPolyDP(contour, epsilon, True)

                    # 判断是否近似封闭（根据顶点数量和形状规则性）
                    if len(approx) >= 4:  # 至少4个顶点，可能是矩形或其他封闭形状
                        closed_regions.append((contour, area, approx))

            # 按面积排序，优先显示较大的封闭区域
            closed_regions.sort(key=lambda x: x[1], reverse=True)

            # 如果找到封闭区域，显示最大的一个
            if closed_regions:
                largest_contour, area, approx = closed_regions[0]
                # 获取轮廓边界框
                x, y, w, h = cv2.boundingRect(largest_contour)
                # 提取并显示封闭区域内的图像
                if w > 20 and h > 20:  # 确保区域足够大
                    cropped_region = frame[y:y + h, x:x + w]

        else:
            # 如果当前没有检测到直线，但还在延时时间内，则使用上一次的结果
            if last_lines is not None and (current_time -
                                           last_valid_time) < delay_duration:
                # 重新绘制上一次的直线
                for line in last_lines:
                    x1, y1, x2, y2 = line[0]
                    cv2.line(line_frame, (x1, y1), (x2, y2), (0, 255, 0), 2)

                # 显示上一次裁剪的图像
                # if last_cropped is not None:

            else:
                # 超过延时时间或没有历史记录，清空历史
                last_lines = None
                last_cropped = None

        # 显示带直线的原始图像
        cv2.imshow('Lines', line_frame)

        # 显示近5秒内最大的识别边界范围
        if cropped_history:
            # 过滤出5秒内的记录 (5000毫秒)
            recent_crops = [
                crop for crop in cropped_history
                if current_time - crop['time'] <= 5000
            ]

            if recent_crops:
                # 在5秒内的记录中找到最大的
                largest_recent = max(recent_crops, key=lambda x: x['area'])
                cv2.imshow('Largest Recent Cropped Area',
                           largest_recent['image'])

        # 按'q'键退出
        if cv2.waitKey(1) & 0xFF == ord('q'):
            break

    # 释放资源
    cap.release()
    cv2.destroyAllWindows()


if __name__ == "__main__":
    detect_edges()
